﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using GameEngine.MoveHistory;
using Daphne.GameShared;

namespace GameEngine
{
    /// <summary>
    /// Class responsible for keeping and managing the currently loaded game
    /// </summary>
    public class GameManager
    {
        /// <summary>
        /// List of PDN 3.0 Tags
        /// </summary>
        private List<string> _lstPDNTags = new List<string>()
        {
            "Event", "Site", "Date", "Round", "White", "Black", "Result", "WhiteTitle", "BlackTitle", "WhiteRating", 
            "BlackRating", "WhiteNA", "BlackNA", "WhiteType", "BlackType", "EventDate", "EventSponsor", "Section", 
            "Stage", "Board", "GameType", "FEN", "PlyCount", "Termination", "TimeControl", "TimeControlWhite",
            "TimeControlBlack", "WhiteTime", "BlackTime", "Time", "UTCTime", "UTCDate", "Annotator", "Author", 
            "Publication", "PublicationDate"
        };



        /// <summary>
        /// Current state on game board
        /// </summary>
        public GameBoard GameBoard
        { get; set; }

        /// <summary>
        /// Rule controling object of selected game type, e.g. Czech, English
        /// </summary>
        public GameRuleController GameRuleController
        { get; set; }

        
        /// <summary>
        /// Object responsible of keeping the track of the game
        /// </summary>
        public MoveHistoryFacade MoveHistory
        { get; set; }

        /// <summary>
        /// Type of checkers played
        /// </summary>
        public enumGameType GameType
        { get; set; }

        /// <summary>
        /// Checks whether the suicidal type of checkers in played
        /// </summary>
        public bool SuicidalCheckers
        { get; set; }

        /// <summary>
        /// Checks whether chess or checkers notation of game match is turned on
        /// </summary>
        public bool NumberNotation
        { get; set; }

        /// <summary>
        /// Returns the side move move
        /// </summary>
        /// <returns></returns>
        public enumFigureColor GetOnMove()
        {
            return GameBoard.OnMove;
        }

        /// <summary>
        /// Game PDN Headers
        /// </summary>
        public Dictionary<string, string> PDNHeaders
        { get; set; }

        /// <summary>
        /// Returns the collection of all existing PDN 3.0 Tags
        /// </summary>
        public List<string> AllPDNTags
        {
            get
            {
                return _lstPDNTags;
            }
        }

        /// <summary>
        /// Specific piece rows for given type of checkers
        /// </summary>
        public int PieceRows
        { get; set; }
            

        /// <summary>
        ///  Starts a new game
        /// </summary>
        /// <param name="manRows"></param>
        public void NewGame()
        {
            GameBoard.SetupStartPosition(PieceRows);

            MoveHistory.ClearMoves();
            PDNHeaders.Clear();
        }


        /// <summary>
        /// Does a move, adds it to the end of game history
        /// </summary>
        /// <param name="move"></param>
        public void DoMove(Move move)
        {
            move.bitOriginalPosition = GameBoard.PackGameBoard();

            GameBoard.DoMove(move);
            MoveHistory.AddMove(move);
            GameBoard.ChangeOnMove();
        }

        /// <summary>
        /// Method for UI to do one move part
        /// </summary>
        /// <param name="move"></param>
        public void DoMoveStep(Move move)
        {
            move.bitOriginalPosition = GameBoard.PackGameBoard();
            GameBoard.DoMove(move);
        }

        /// <summary>
        /// Undoes the move step
        /// </summary>
        public void UndoMoveStep(Move move)
        {
            GameBoard.UndoMove(move);
        }


        /// <summary>
        /// Method for UI, finalizing the move done
        /// </summary>
        /// <param name="move"></param>
        public void FinalizeMove(Move move)
        {
            MoveHistory.AddMove(move);
            GameBoard.ChangeOnMove();
        }

        /// <summary>
        /// Determines whether the figure can move
        /// </summary>
        public bool CanThisFigureMove(Position pos, out List<Position> jumpPosList)
        {
            return GameRuleController.CanThisFigureMove(pos, out jumpPosList);
        }


        /// <summary>
        /// Can the figure move to this field?
        /// </summary>
        public bool CanFigureMoveHere(Position start, Position end, bool oMultipleJump = false)
        {
            return GameRuleController.CanFigureMoveHere(start, end, oMultipleJump);
        }

        /// <summary>
        /// Completes the move step
        /// </summary>
        public Move CompleteMoveStep(Position start, Position end)
        {
            return GameRuleController.CompleteMoveStep(start, end);
        }

        /// <summary>
        /// Determines whether we should continue in jumping
        /// </summary>
        public bool DoesMoveContinue(Move previousMove, Position pos)
        {
            return GameRuleController.MustFigureContinueWithJump(previousMove, pos);
        }

        /// <summary>
        /// Get figure multiple jumps
        /// </summary>
        public void GetFigureMultipleJumps(Position pos, List<Position> moves)
        {
            GameRuleController.GetFigureMoves(pos, moves, true);
        }

        /// <summary>
        /// Returns the list of places where given figure can move
        /// </summary>
        public void GetFigureMoves(Position pos, List<Position> moves)
        {
            GameRuleController.GetFigureMoves(pos, moves);
        }

        /// <summary>
        /// Returns positions of figures which can do move
        /// </summary>
        /// <returns></returns>
        public List<Position> GetFiguresToMove()
        {
            List<Position> lstFiguresToMove = new List<Position>();

            for(int row = 0; row < GameBoard.Size; row++)
                for (int col = 0; col < GameBoard.Size; col++)
                {
                    Position pos = new Position(row, col);
                    if (GameBoard.GetField(pos) != enumFigureType.Nothing)
                    {
                        // if figure can move, add it to list
                        List<Position> lstDummy = null;
                        if (GameRuleController.CanThisFigureMove(pos, out lstDummy) == true)                       
                        {
                            lstFiguresToMove.Add(pos);


                        } // if
                    } // if
                } // for

            return lstFiguresToMove;
        }

        /// <summary>
        /// Generates all possible moves from position of gameboard
        /// </summary>
        public List<Move> GenerateAllMoves()
        {
            List<Move> moves = new List<Move>();

            // get all figures to move
            List<Position> figuresToMove = GetFiguresToMove();

            // get the moves for all figures
            foreach (Position figurePos in figuresToMove)
            {
                moves.AddRange(GenerateMovesForFigure(figurePos));
            }

            return moves;
        }

        /// <summary>
        /// Generates all possible moves for given figure
        /// </summary>
        private List<Move> GenerateMovesForFigure(Position figurePos)
        {
            List<Move> moves = new List<Move>();

            // get all possible positions where figure can move
            List<Position> nextPositions = new List<Position>();
            GetFigureMoves(figurePos, nextPositions);

            // iterate through all possible positions
            foreach (var position in nextPositions)
            {
                moves.AddRange(ProbeMoves(figurePos, position));
            }

            return moves;
        }

        /// <summary>
        /// Recursively probes all necessary moves
        /// </summary>
        private List<Move> ProbeMoves(Position startPos, Position endPos)
        {
            List<Move> moves = new List<Move>();

            // complete move step
            Move rootMoveStep = CompleteMoveStep(startPos, endPos);
            if (rootMoveStep.RemovingFigure == false)
            {
                moves.Add(rootMoveStep);
                return moves;
            }

            DoMoveStep(rootMoveStep);

            // get succeeding jumps
            List<Position> succeedingJumps = new List<Position>();
            GetFigureMultipleJumps(endPos, succeedingJumps);

            // if we have reached the end of the move
            if (succeedingJumps.Count == 0)
            {
                moves.Add(rootMoveStep);
            }
            else
            {
                // else probe following capturing positions
                foreach (var followingJump in succeedingJumps)
                {
                    // get all other moves
                    List<Move> nextJumps = ProbeMoves(endPos, followingJump);

                    // create local copy of all branching moves
                    foreach (var nextMove in nextJumps)
                    {
                        Move copyMove = rootMoveStep.Copy();
                        copyMove.nextMove = nextMove;
                        moves.Add(copyMove);
                    }
                }
            }

            // undo the move step
            UndoMoveStep(rootMoveStep);

            return moves;
        }

        /// <summary>
        /// Investigates the move from given only by source and target field
        /// Used to fill in the intermediate fields
        /// </summary>
        public List<Move> InvestigateMove(Position origPos, Position targetPos)
        {
            // init vars
            List<Move> moves = new List<Move>();
            Move localMove = null;

            // get succeeding moves
            List<Position> nextTargetFields = new List<Position>();
            GetFigureMoves(origPos, nextTargetFields);

            // iterate all positions
            foreach(Position nextField in nextTargetFields)
            {
                // we have reached the target field
                if(nextField == targetPos)
                {
                    localMove = CompleteMoveStep(origPos, targetPos);
                    if (localMove.RemovingFigure == true)
                    {
                        moves.Add(localMove);
                    }
                }
                else
                {
                    // complete intermediate move step
                    localMove = CompleteMoveStep(origPos, nextField);
                    
                    // do move step
                    DoMoveStep(localMove);

                    if (localMove.RemovingFigure == true)
                    {
                        // go deeper
                        List<Move> followingMoves = InvestigateMove(nextField, targetPos);

                        // clone the moves if some found
                        foreach (Move followMove in followingMoves)
                        {
                            Move copyMove = localMove.Copy();
                            copyMove.nextMove = followMove;
                            moves.Add(copyMove);
                        }
                    }

                    // undo move step
                    UndoMoveStep(localMove);
                }
            }

            // return the resulting moves
            return moves;
        }

        public bool TryParsePosition(string sPosition, out Position pos)
        {
            return GameRuleController.TryParsePosition(sPosition, out pos);
        }

        public string ToNumberNotation(Position pos)
        {
            return GameRuleController.ToNumberNotation(pos);
        }

        public string MoveToString(Move move, bool bNumberNotation)
        {
            StringBuilder builder = new StringBuilder();           
            builder.Append(bNumberNotation == false ? move.startPos.ToString() : ToNumberNotation(move.startPos));
            if (move.RemovingFigure == true)
                builder.Append(" x ");
            else
                builder.Append(" - ");
            builder.Append(bNumberNotation == false ? move.endPos.ToString() : ToNumberNotation(move.endPos));

            Move otherMove = move.nextMove;
            while (otherMove != null)
            {
                builder.Append(" x ");
                builder.Append(bNumberNotation == false ? otherMove.endPos.ToString() :  ToNumberNotation(otherMove.endPos));
                otherMove = otherMove.nextMove;
            }

            if (string.IsNullOrWhiteSpace(move.MoveStrength) == false)
            {
                builder.Append(" ");
                builder.Append(move.MoveStrength.Trim());
            }

            return builder.ToString();
        }

        public enumGameResult EvaluateGameResult()
        {
            List<Move> moveHistory = new List<Move>();
            MoveHistory.GetMovesDone(moveHistory);
            return GameRuleController.EvaluateGameResult(moveHistory);
        }
    }
}
